package fag.core.game;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public abstract class AbstractGame extends Thread implements Runnable
{	
	public static long MINIMUM_INTERVAL = 13;
	
	private boolean run = true;
	
	private List<Scene> scenes = Collections.synchronizedList(new ArrayList<Scene>());
	
	private List<Double> stepTrace = new ArrayList<Double>();
	
	/**
	 * Constructeur par défaut.
	 */
	public AbstractGame() {
	}
	
	/**
	 * Ajouter une scène à ce moteur de jeu.
	 * @param scene La scène à ajouter.
	 * @return Vrai si la liste des scènes a changé, sinon faux.
	 */
	public boolean addScene(Scene scene) {
		return scenes.add(scene);
	}
	
	/**
	 * Obtenir le fps moyen de la dernière seconde.
	 * @return Le fps moyen de la dernière seconde.
	 */
	public int getAverageFps() {
		
		double sum = 0;
		for(double dt : stepTrace)
			sum += dt;
		
		if(sum > 1000)
			stepTrace.remove(0);
		
		double average = sum / stepTrace.size();
		return (int)(1000 / average);
	}
	
	/**
	 * Retirer une scène à ce moteur de jeu.
	 * @param scene La scène à retirer.
	 * @return Vrai si la liste des scènes à changé sinon faux.
	 */
	public boolean removeScene(final Scene scene) {
		return scenes.remove(scene);
	}
	
	/**
	 * Boucle du jeu.
	 */
	public final void run() {
		
		long dt = MINIMUM_INTERVAL;
		long timestamp = 0;
	
		while(run) {
			
			timestamp = System.currentTimeMillis();
			
			synchronized(scenes) {
				for(Scene scene : scenes) {
					scene.step(dt);
					scene.rebuildQuadtree();
				}
			}
			
			// Point d'extension
			step(dt);
			
			// Calcul du prochain dt et maîtrise du planché
			dt = System.currentTimeMillis() - timestamp;
			
			if(dt < MINIMUM_INTERVAL) {
				
				dt = MINIMUM_INTERVAL;			
			
				try {
					sleep(dt);
				}
				catch (InterruptedException e) {
	                System.out.println("interrupted");
	            }
			}
			
			// Trace pour le calcul du fps
			stepTrace.add(new Double(dt));
        }
	}
	
	/**
	 * Point d'extension du framework.
	 * @param dt L'intervalle de temps depuis la dernière exécution.
	 */
	public abstract void step(double dt);
}
